/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

#ifndef SERVERENGINE_COMMON_H
#define SERVERENGINE_COMMON_H

#include <cassert>
#include <cerrno>
#include <cmath>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <unordered_map>
#include <unordered_set>

#if AC_PLATFORM == AC_PLATFORM_WINDOWS
#define STRCASECMP stricmp
#else
#define STRCASECMP strcasecmp
#endif

#include <set>
#include <list>
#include <string>
#include <map>
#include <queue>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <vector>

#if AC_COMPILER == AC_COMPILER_MICROSOFT

#include <float.h>

#define I32FMT "%08I32X"
#define I64FMT "%016I64X"
#define atoll _atoi64
#define vsnprintf _vsnprintf
#define llabs _abs64

#else

#define stricmp strcasecmp
#define strnicmp strncasecmp
#define I32FMT "%08X"
#define I64FMT "%016llX"

#endif

#define MAX_QUERY_LEN 32*1024

using namespace std;

inline float finiteAlways(float f) { return isfinite(f) ? f : 0.0f; }

inline bool myisfinite(float f) { return isfinite(f) && !isnan(f); }

#define atol(a) strtoul( a, NULL, 10)

enum TimeConstants
{
    MINUTE          = 60,
    HOUR            = MINUTE * 60,
    DAY             = HOUR * 24,
    WEEK            = DAY * 7,
    MONTH           = DAY * 30,
    YEAR            = MONTH * 12,
    IN_MILLISECONDS = 1000
};

typedef std::vector<std::string> StringVector;

template<class T, size_t MAX_CACHE_SIZE = 0>
class CBufferPtrT
{
public:
    explicit CBufferPtrT(size_t size = 0, bool zero = false) { Reset(); Malloc(size, zero); }
    explicit CBufferPtrT(const T* pch, size_t size) { Reset(); Copy(pch, size); }
    CBufferPtrT(const CBufferPtrT& other) { Reset(); Copy(other); }
    template<size_t S> CBufferPtrT(const CBufferPtrT<T, S>& other) { Reset(); Copy(other); }

    ~CBufferPtrT() { Free(); }

    T* Malloc(size_t size = 1, bool zero = false)
    {
        Free();
        return Alloc(size, zero, false);
    }

    T* Realloc(size_t size, bool zero = false)
    {
        return Alloc(size, zero, true);
    }

    void Free()
    {
        if (m_pch)
        {
            free(m_pch);
            Reset();
        }
    }

    template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, S>& other)
    {
        if ((void*)&other != (void*)this)
            Copy(other.Ptr(), other.Size());

        return *this;
    }

    CBufferPtrT& Copy(const T* pch, size_t size)
    {
        Malloc(size);

        if (m_pch)
            memcpy(m_pch, pch, size * sizeof(T));

        return *this;
    }

    template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
    {
        if ((void*)&other != (void*)this)
            Cat(other.Ptr(), other.Size());

        return *this;
    }

    CBufferPtrT& Cat(const T* pch, size_t size = 1)
    {
        size_t pre_size = m_size;
        Realloc(m_size + size);

        if (m_pch)
            memcpy(m_pch + pre_size, pch, size * sizeof(T));

        return *this;
    }

    template<size_t S> bool Equal(const CBufferPtrT<T, S>& other) const
    {
        if ((void*)&other == (void*)this)
            return true;
        else if (m_size != other.Size())
            return false;
        else if (m_size == 0)
            return true;
        else
            return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
    }

    bool Equal(T* pch) const
    {
        if (m_pch == pch)
            return true;
        else if (!m_pch || !pch)
            return false;
        else
            return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
    }

    size_t SetSize(size_t size)
    {
        if (size < 0 || size > m_capacity)
            size = m_capacity;

        return (m_size = size);
    }

    T* Ptr() { return m_pch; }
    const T* Ptr()			const { return m_pch; }
    T& Get(int i) { return *(m_pch + i); }
    const T& Get(int i)		const { return *(m_pch + i); }
    size_t		Size()			const { return m_size; }
    size_t		Capacity()		const { return m_capacity; }
    bool		IsValid()		const { return m_pch != 0; }

    operator T* () { return Ptr(); }
    operator const						T* ()			const { return Ptr(); }
    T& operator							[]	(int i) { return Get(i); }
    const T& operator					[]	(int i)		const { return Get(i); }
    bool operator						==	(T* pv)		const { return Equal(pv); }
    template<size_t S> bool operator	==	(const CBufferPtrT<T, S>& other) { return Equal(other); }
    CBufferPtrT& operator				=	(const CBufferPtrT& other) { return Copy(other); }
    template<size_t S> CBufferPtrT& operator = (const CBufferPtrT<T, S>& other) { return Copy(other); }

private:
    void Reset() { m_pch = 0; m_size = 0; m_capacity = 0; }
    size_t GetAllocSize(size_t size) { return max(size, min(size * 2, m_size + MAX_CACHE_SIZE)); }

    T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
    {
        if (size != m_size)
        {
            size_t rsize = GetAllocSize(size);
            if (size > m_capacity || rsize < m_size)
            {
                T* pch = is_realloc ?
                    (T*)realloc(m_pch, rsize * sizeof(T)) :
                    (T*)malloc(rsize * sizeof(T));

                if (pch || rsize == 0)
                {
                    m_pch = pch;
                    m_size = size;
                    m_capacity = rsize;
                }
                else
                {
                    Free();
                    throw std::bad_alloc();
                }
            }
            else
                m_size = size;
        }

        if (zero && m_pch)
            memset(m_pch, 0, m_size * sizeof(T));

        return m_pch;
    }

private:
    T* m_pch;
    size_t	m_size;
    size_t	m_capacity;
};
typedef CBufferPtrT<char>			CCharBufferPtr;
typedef CBufferPtrT<wchar_t>		CWCharBufferPtr;
typedef CBufferPtrT<unsigned char>	CByteBufferPtr;
typedef CByteBufferPtr				CBufferPtr;

namespace acore
{
    template<class ArgumentType, class ResultType>
    struct unary_function
    {
        typedef ArgumentType argument_type;
        typedef ResultType result_type;
    };
}

#endif
